function [Price, AccruedInt] = bndprice(Yield, CouponRate, Settle, Maturity,varargin)
%BNDPRICE Price a fixed income security from yield to maturity.
%   Given NBONDS with date parameters and yields to
%   maturity, return the clean prices and the accrued interest due.
%
%   [Price, AccruedInt] = bndprice(Yield, CouponRate, Settle, Maturity)
%
%   [Price, AccruedInt] = bndprice(Yield, CouponRate, Settle, Maturity, ...
%       Period, Basis, EndMonthRule, IssueDate, FirstCouponDate, ...
%       LastCouponDate, StartDate, Face)
%
%   [Price, AccruedInt] = bndprice(Yield, CouponRate, Settle, Maturity,
%      'Param1','Value1',...)
%
%   Optional Inputs: Period, Basis, EndMonthRule, IssueDate, FirstCouponDate,
%                    LastCouponDate, StartDate, Face, LastCouponInterest,
%                    CompoundingFrequency, DiscountBasis
%
%   Note:
%   - For SIA conventions, the Price and Yield are related by the formula:
%     Price + Accrued Interest = sum(Cash_Flow*(1+Yield/2)^(-Time))
%     where the sum is over the bond's cash flows and corresponding times in
%     units of semi-annual coupon periods.
%     For ISMA conventions, the Price and Yield are related by the formula:
%     Price + Accrued Interest = sum(Cash_Flow*(1+Yield)^(-Time))
%   - All non-scalar or empty matrix input arguments must be either NUMBONDSx1
%     or 1xNUMBONDS conforming vectors.
%   - Fill unspecified entries in input vectors with NaN.
%   - Dates can be serial date numbers or date strings.
%   - Optional inputs can be specified as parameter value pairs.  If
%     LastCouponInterest, CompoundingFrequency or DiscountBasis are input,
%     optional inputs must be specified as parameter value pairs.
%     Otherwise, optional inputs may be specified by order according to the
%     help.
%
%   Inputs:
%        Yield - Yield to maturity.
%
%   CouponRate - Coupon rate in decimal form.
%
%       Settle - Settlement date.
%
%     Maturity - Maturity date.
%
%   Optional Inputs:
%            Period - Number of coupons payments per year.
%                     Possible values include:
%                     0, 1, 2 (default), 3, 4, 6, 12
%
%             Basis - Day-count basis.
%                     Possible values include:
%                     0 - actual/actual (default)
%                     1 - 30/360 SIA
%                     2 - actual/360
%                     3 - actual/365
%                     4 - 30/360 PSA
%                     5 - 30/360 ISDA
%                     6 - 30/360 European
%                     7 - actual/365 Japanese
%                     8 - actual/actual ISMA
%                     9 - actual/360 ISMA
%                    10 - actual/365 ISMA
%                    11 - 30/360 ISMA
%                    12 - actual/365 ISDA
%                    13 - bus/252
%
%      EndMonthRule - End-of-month rule; default is 1 (in effect)
%                     0 - Rule is NOT in effect for the bond(s)
%                     1 - (default) Rule is in effect for the bond(s) (meaning
%                         that a security that pays coupon interest on the last
%                         day of the month will always make payment on the last
%                         day of the month)
%
%         IssueDate - Bond issue date.
%
%   FirstCouponDate - Irregular or normal first coupon date.
%
%    LastCouponDate - Irregular or normal last coupon date.
%
%         StartDate - Forward starting date of payments.
%
%              Face - Face value of the bond; default is 100.
%
%   LastCouponInterest - Compounding convention for computing the yield of
%                        a bond in the last coupon period, i.e.: with only
%                        the last coupon and the face value to be repaid.
%                        Choices are 'simple' or 'compound'.
%
%   CompoundingFrequency - Compounding frequency for yield calculation.  By
%                          default, SIA bases (0-7) and BUS/252 use a semi-annual
%                          compounding convention and ISMA bases (8-12) use
%                          an annual compounding convention.
%
%   DiscountBasis - Basis used to compute the discount factors for
%                   computing the yield.  The default behavior is for SIA
%                   bases to use the actual/actual day count to compute
%                   discount factors, and for ISMA day counts and BUS/252
%                   to use the specified basis.
%
%   Outputs:
%   Price - [NUMBONDSx1 vector] Clean price of the bond.
%           The dirty price of the bond is the clean price plus the accrued
%           interest. It is equal to the present value of the bond cash flows.
%
%   AccruedInt - [NUMBONDSx1 vector] Accrued interest payable at settlement.
%
%   See also BNDYIELD, CFAMOUNTS

%  Copyright 1995-2009 The MathWorks, Inc.
%  $Revision: 1.11.2.11 $   $Date: 2009/11/05 16:59:00 $

% Checking input arguments
if nargin < 4
    error('Finance:bndprice:tooFewInputs', 'Too few inputs.');
end

% Check to see whether we have the case of ordered inputs or PV pairs
if ~isempty(varargin)
    if ischar(varargin{1})
        
        p = inputParser;
        
        p.addParamValue('discountbasis',NaN,@isvalidbasis);
        p.addParamValue('compoundingfrequency',NaN,@(x) ismember(x,[-1 1 2 3 4 6 12]));
        p.addParamValue('lastcouponinterest',{'compound'},...
            @(x) ismember(x,{'compound','simple'}));
        
        p.addParamValue('period',[]);
        p.addParamValue('basis',[]);
        p.addParamValue('endmonthrule',[]);
        p.addParamValue('issuedate', []);
        p.addParamValue('firstcoupondate', []);
        p.addParamValue('lastcoupondate', []);
        p.addParamValue('startdate', []);
        p.addParamValue('face',[]);
        
        try
            p.parse(varargin{:});
        catch ME
            newME = MException('finance:bndprice:optionalInputError',...
                'Error in input arguments');
            newME = addCause(newME,ME);
            throw(newME)
        end
        
        Frequency = p.Results.compoundingfrequency;
        LastCouponInterest = p.Results.lastcouponinterest;
        Basis =  p.Results.basis;
        DiscountBasis = p.Results.discountbasis;
        
        try
            [CouponRate, Settle, Maturity,~,Basis] = instargbond(CouponRate, Settle, Maturity,[], Basis);
        catch ME
            throwAsCaller(ME)
        end
    else
        Frequency = NaN;
        LastCouponInterest = {'compound'};
        DiscountBasis = NaN;
        
        try
            [CouponRate, Settle, Maturity,~,Basis] = instargbond(CouponRate, Settle, Maturity,varargin{:});
        catch ME
            throwAsCaller(ME)
        end
    end
else
    
    Frequency = NaN;
    LastCouponInterest = {'compound'};
    DiscountBasis = NaN;
    
    try
        [CouponRate, Settle, Maturity,~,Basis] = instargbond(CouponRate, Settle, Maturity);
    catch ME
        throwAsCaller(ME)
    end
end

% Strip out lastcouponinterest
if any(strcmpi(varargin,'lastcouponinterest'))
    lciidx = find(strcmpi(varargin,'lastcouponinterest'));
    varargin([lciidx lciidx+1]) = [];
end

% The scalar expansion done inside instargbond may not be correct since it
% doesn't consider "Yield".
try
    [CouponRate, Settle, Maturity, Yield, Frequency, LastCouponInterest, DiscountBasis] = ...
        finargsz(1, CouponRate, Settle, Maturity, Yield, Frequency, ...
        LastCouponInterest, DiscountBasis);
catch ME
    throwAsCaller(ME)
end

% Call cfamounts for bond parameters
[CFlowAmounts, ~, TFactors] = cfamounts(CouponRate, Settle, Maturity, varargin{:});

[~, MaxNumCFs] = size(CFlowAmounts);

% The first computed cash flow is the accrued interest
AccruedInt = -CFlowAmounts(:, 1);

nBonds = length(Yield);

% use semi-anual compounding frequency for yields for SIA conventions
% use annual compounding frequency for yields for ISMA convention
if isnan(Frequency)
    Frequency = 2*ones(nBonds,1);
    if isnan(DiscountBasis)
        i = isisma(Basis);
    else
        i = isisma(DiscountBasis);
    end
    Frequency(i) = 1;
end

PerDisc = 1./(1 + Yield./Frequency);

% Compute the discounts from settlement for every cash flow
DiscountRates = PerDisc(:, ones(1, MaxNumCFs)).^TFactors;

% Compute the present value of every cash flow (including accrued
% interest payment at settlement)
CFlowPVs = CFlowAmounts .* DiscountRates;

% Sum the present value cash flows along the rows to get the price
Price = nansum(CFlowPVs, 2);

% Put this in for last coupon
lastcouponidx = sum(~isnan(CFlowAmounts),2) == 2;
if any(lastcouponidx)
    simpleidx = strcmpi(LastCouponInterest,'simple') & lastcouponidx;
    
    if any(simpleidx)
        Price(simpleidx) = (CFlowAmounts(simpleidx,2))./...
            (1 + Yield(simpleidx)./Frequency(simpleidx).*TFactors(simpleidx,2))...
            + CFlowAmounts(simpleidx,1);
    end
end